package github.ziwukun.jsonql.exe;


import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import github.ziwukun.jsonql.analysis.ModelAndQLAnalysistorManager;
import github.ziwukun.jsonql.analysis.ModelAndQLAnalysistorManagers;
import github.ziwukun.jsonql.lang.JSONQL;
import github.ziwukun.jsonql.lang.Select;
import github.ziwukun.jsonql.lang.SelectItem;
import github.ziwukun.jsonql.lang.Where;
import github.ziwukun.jsonql.orm.ContainerType;
import github.ziwukun.jsonql.orm.CtnProp;
import github.ziwukun.jsonql.orm.FmProp;
import github.ziwukun.jsonql.orm.JoinProp;
import github.ziwukun.jsonql.orm.JoinPropCtn;
import github.ziwukun.jsonql.orm.Model;
import github.ziwukun.jsonql.orm.PropConverter;
import github.ziwukun.jsonql.orm.RltProp;
import github.ziwukun.jsonql.utils.CollectionUtils;
import github.ziwukun.jsonql.utils.ModelUtils;

/**
 * 
 * @author Hugo.Yu
 */
public class JQLExecutorImpl implements JQLExecutor{
	private ModelAndQLAnalysistorManager analysistorMng;
	private Map<ContainerType, CtnQLExecutor> ctnExecutors = new HashMap<>();
	
	// TODO 优化创建方式
	JQLExecutorImpl(List<CtnQLExecutor> exes){
		this.analysistorMng = ModelAndQLAnalysistorManagers.manager();
		for (CtnQLExecutor exe : exes) {
			ctnExecutors.put(exe.support(), exe);
		}
	}
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Override
	public String execut(Model model, JSONQL ql) {
		// 分析语法错误
		analysistorMng.analysis(model, ql);
		
		// 分析出容器的单表查询语句
		JSONQL ctnQL = JQLUtils.ctnPropJQL2CtnJQL(model.ctn().ctnName(),model.listCtnProps(),ql);
		
		// 确保会搜索id
		if(!ctnQL.select().containProp(model.id().ctnField().fieldName())) {
			ctnQL = ctnQL.repSelect(Select.link(ctnQL.select(), 
				Select.builder().props(model.id().ctnField().fieldName()).build()));
		}
		
		// 根据其他容器的搜索条件拼接新的条件
		List<Where> realWheres = new LinkedList<>();
		if(ctnQL.where() != null) {realWheres.add(ctnQL.where());}
		
		// 2 解析连接容器的属性条件语句
		Map<JoinPropCtn, List<JoinProp>> ctnAndProps = ModelUtils.mapJoinProps(model);
		if(!model.isJoinPropEmpty()) {
			for (Map.Entry<JoinPropCtn, List<JoinProp>> kv : ctnAndProps.entrySet()) {
				JoinPropCtn jpc = kv.getKey();
				List<JoinProp> props = kv.getValue();
				
				// user[detailId] userDetail
				// 		1 先查出符合条件的userDetail的Id
				// 		2 detailId IN
				if(jpc.mappedBy() == model.ctn()) {
					// 构建JSON QL 先查出符合条件的userDetail的Id
					String idName = jpc.id().ctnField().fieldName();
					JSONQL inJql = JSONQL.builder()
						.select(select -> {
							select.props(idName);})
						.from(jpc.ctnName())
						.build();
					Where inWhere = JQLUtils.ctnPropJWhere2CtnFieldJWhere(props,ql);
					// 没有限制条件
					if(inWhere == null) {
						continue;
					}
					inJql = inJql.repWhere(inWhere);
					CtnQLExecutor executor = ctnExecutors.get(jpc.ctnType());
					List<Map> inResult = executor.exe(inJql);
					List<Object> inIds = inResult.stream().map(item -> {
						return item.get(idName);
					}).collect(Collectors.toList());
					
					// 设置IN条件
					realWheres.add(Where.builder()
						.in(jpc.joinField().fieldName(), inIds)
						.build());
					
					// 补充搜索一列
					Select compSelect = Select.link(ctnQL.select(), Select.builder()
						.props(jpc.joinField().fieldName()).build());
					ctnQL = ctnQL.repSelect(compSelect);
				}
				
				// user userDetail[userId]
				// 		1 先查出符合条件的userDetail的userId
				// 		2 id IN
				else {
					// 构建JSON QL 先查出符合条件的userDetail的Id
					String joinField = jpc.joinField().fieldName();
					JSONQL inJql = JSONQL.builder()
						.select(select -> {
							select.props(joinField);})
						.from(jpc.ctnName())
						.build();
					Where inWhere = JQLUtils.ctnPropJWhere2CtnFieldJWhere(props,ql);
					// 没有限制条件
					if(inWhere == null) {
						continue;
					}
					inJql = inJql.repWhere(inWhere);
					
					CtnQLExecutor executor = ctnExecutors.get(jpc.ctnType());
					List<Map> inResult = executor.exe(inJql);
					List<Object> inIds = inResult.stream().map(item -> {
						return item.get(joinField);
					}).collect(Collectors.toList());
					
					// 设置IN条件
					realWheres.add(Where.builder()
						.in(model.ctn().id().ctnField().fieldName(), inIds)
						.build());
				}
			}
		}
		
		// 3 解析容器连接属性的条件语句
		// UserDetail表可能是一个复合容器，可能有递归
		for (FmProp fmProp : model.listFmProps()) {
			Where propWhere = ql.where().propWhere(fmProp.propName());
			// 没有搜索条件 不需要向下搜索
			if(propWhere == null) {
				continue;
			}
			
			// model保存关系键
			// user[detailId] userDetail
			// 1 先查出符合条件的userDetail的Id
			// 2 detailId IN 
			if(fmProp.linkProp().ofModel() == model) {
				JSONQL inJql = JSONQL.builder()
					.select(select -> {
						select.props(fmProp.propModel()
							.id().propName());})
					.from(fmProp.propModel().modelName())
					.build();
				inJql = inJql.repWhere(propWhere);
				String inJson = execut(fmProp.propModel(),inJql);
				JSONArray inIds = new JSONArray(inJson);
				
				// 设置IN条件
				realWheres.add(Where.builder()
					.in(fmProp.linkProp().ctnField().fieldName(), inIds)
					.build());
			}
			// user userDetail[userId]
			// 1 先查出符合条件的userDetail的userId
			// 2 id IN
			else {
				JSONQL inJql = JSONQL.builder()
					.select(select -> {
						select.props(fmProp.linkProp().propName());})
					.from(fmProp.propModel().modelName())
					.build();
				inJql = inJql.repWhere(propWhere);
				String inJson = execut(fmProp.propModel(),inJql);
				JSONArray inIds = new JSONArray(inJson);
				
				// 设置IN条件 id IN
				realWheres.add(Where.builder()
					.in(model.id().ctnField().fieldName(), inIds)
					.build());
			}
		}
		
		// 4 解析关系关联的属性条件语句
		for (RltProp rltProp : model.listRltProps()) {
			// 1 先查符合条件的属性Id数据
			Where propWhere = ql.where().propWhere(rltProp.propName());
			// 没有搜索条件 不需要向下搜索
			if(propWhere == null) {
				continue;
			}
			
			JSONQL inJql = JSONQL.builder()
				.select(select -> {
					select.props(rltProp.propModel().id().propName());})
				.from(rltProp.propModel().modelName())
				.build();
			inJql = inJql.repWhere(propWhere);
			String propModelIdsJson = execut(rltProp.propModel(),inJql);
			JSONArray propModelIds = new JSONArray(propModelIdsJson);
			
			// 从关联表中查出模型的id
			JSONQL rltInJql = JSONQL.builder()
				.select(select -> {
					select.props(rltProp.primaryField().fieldName());})
				.from(rltProp.rltCtn().ctnName())
				.where(where -> {
					where.in(rltProp.foreignField().fieldName(), propModelIds);
				})
				.build();
			CtnQLExecutor executor = ctnExecutors.get(rltProp.rltCtn().ctnType());
			List<Map> idInMap =  executor.exe(rltInJql);
			List<Object> inIds = idInMap.stream().map(item -> {
				return item.get(rltProp.primaryField().fieldName());
			}).collect(Collectors.toList());
			
			// 设置IN条件
			realWheres.add(Where.builder()
				.in(model.ctn().id().ctnField().fieldName(), inIds)
				.build());
		}
		
		// 替换成补充过后的where语句
		ctnQL = ctnQL.repWhere(Where.and(realWheres));
		// 查询出所有的模型的容器数据
		CtnQLExecutor executor = ctnExecutors.get(model.ctn().ctnType());
		List<Map> modelCtnMap = executor.exe(ctnQL);
		
		// 继续向下查询，补充来至于其他容器的数据
		for (Map.Entry<JoinPropCtn, List<JoinProp>> kv : ctnAndProps.entrySet()) {
			JoinPropCtn jpc = kv.getKey();
			List<JoinProp> props = kv.getValue();
			
			// 从容器需要搜索的属性列表
			List<SelectItem> selectProps = new LinkedList<>();
			Select select = ql.select();
			for (SelectItem selectProp : select.listItems()) {
				if(CollectionUtils.contains(props, jp -> {
					return jp.propName().equals(selectProp.prop());
				})) {
					selectProps.add(selectProp);
				}
			}
			if(selectProps.isEmpty()) {
				continue;
			}
			
			Select propSelect = Select.newInstance(selectProps);
			// user[detailId] userDetail
			// 1 从主容器中的搜索结果中找出链接列的值
			// 2 搜索从容器的数据
			// 3 补充属性值
			if(jpc.mappedBy() == model.ctn()) {
				// 1 从主容器中的搜索结果中找出链接列的值
				List<Object> propCtnIds = new LinkedList<>();
				for (Map ctnRecord : modelCtnMap) {
					Object id = ctnRecord.get(jpc.joinField().fieldName());
					propCtnIds.add(id);
				}
				
				// 多搜索一列Id
				Select fieldSelect = JQLUtils.ctnPropJSelect2CtnFieldJSelect(props, propSelect);
				String idFieldName = jpc.id().ctnField().fieldName();
				Select idSelect = Select.builder().props(idFieldName).build();
				fieldSelect = Select.link(fieldSelect, idSelect);
				
				JSONQL joinQl = JSONQL.newInstance(
					fieldSelect,
					jpc.ctnName(),
					Where.builder()
						.in(jpc.id().ctnField().fieldName(), propCtnIds)
						.build());
				CtnQLExecutor joinCtnExecutor = ctnExecutors.get(jpc.ctnType());
				List<Map> joinRecords = joinCtnExecutor.exe(joinQl);
				
				for (Map ctnRecord : joinRecords) {
					Object id = ctnRecord.get(idFieldName);
					Map map = CollectionUtils.findOne(modelCtnMap, cr -> {
						// TODO 这里的Id的比较方式可以改进
						return cr.get(jpc.joinField().fieldName())
							.toString().equals(id.toString());
					});
					
					map.putAll(ctnRecord);
				}
			}
			// user userDetail[userId]
			// 1 从主容器中的搜索结果中找出ID的值
			// 2 搜索从容器的数据 补充一列主表的id
			// 3 补充属性值
			else {
				// 1 从主容器中的搜索结果中找出链接列的值
				List<Object> modelCtnIds = new LinkedList<>();
				for (Map ctnRecord : modelCtnMap) {
					Object id = ctnRecord.get(model.id().ctnField().fieldName());
					modelCtnIds.add(id);
				}
				
				// 多搜索一列 链接列
				Select fieldSelect = JQLUtils.ctnPropJSelect2CtnFieldJSelect(props, propSelect);
				String joinFieldName = jpc.joinField().fieldName();
				Select joinFieldSelect = Select.builder().props(joinFieldName).build();
				fieldSelect = Select.link(fieldSelect, joinFieldSelect);
				
				JSONQL joinQl = JSONQL.newInstance(
					fieldSelect,
					jpc.ctnName(),
					Where.builder()
						.in(joinFieldName, modelCtnIds)
						.build());
				CtnQLExecutor joinCtnExecutor = ctnExecutors.get(jpc.ctnType());
				List<Map> joinRecords = joinCtnExecutor.exe(joinQl);
				
				for (Map ctnRecord : joinRecords) {
					Object modelId = ctnRecord.get(joinFieldName);
					Map map = CollectionUtils.findOne(modelCtnMap, cr -> {
						// TODO 这里的Id的比较方式可以改进
						return cr.get(model.id().ctnField().fieldName())
							.toString().equals(modelId.toString());
					});
					
					map.putAll(ctnRecord);
				}
			}
		}
		
		
		// 转换成模型
		// TODO 考虑存在子属性的情况
		JSONArray models = new JSONArray();
		for (Map ctnRecord : modelCtnMap) {
			JSONObject modelRecord = new JSONObject(true);
			
			Set<Map.Entry<?,?>> entrys = ctnRecord.entrySet();
			for (Map.Entry<?,?> entry : entrys) {
				String fieldName = entry.getKey().toString();
				Object value = entry.getValue();
				
				CtnProp prop = getCtnPropByFieldName(model,fieldName);
				if(prop != null) {
					// 转换数据类型
					// TODO 考虑递归的情况
					modelRecord.set(prop.propName(),
						PropConverter.getConverter(prop.converter()).convert(value));
				}
				// 容器中存在但是模型中不存在的字段 不管
			}
			
			models.add(modelRecord);
		}
		
		for (FmProp fmProp : model.listFmProps()) {
			// 如果搜索了外键链接模型 就补充外键链接模型的属性值
			if(!ql.select().containProp(fmProp.propName())) {
				continue;
			}
			
			// model保存关系键
			// user[detailId] userDetail
			// 这种情况只能有单属性模型
			if(fmProp.linkProp().ofModel() == model) {
				// userDetailIds
				List<Object> fmPropModelIds = new LinkedList<>();
				for (Map ctnRecord : modelCtnMap) {
					Object id = ctnRecord.get(
						fmProp.linkProp().ctnField().fieldName());
					fmPropModelIds.add(id);
				}
				
				// 搜索属性值
				Select fmSelect = ql.select().propSelect(fmProp.propName());
				fmSelect = Select.link(fmSelect, Select.builder()
					.props(fmProp.propModel().id().propName()).build());
				Where fmWhere = Where.builder()
					.in(fmProp.propModel().id().propName(), fmPropModelIds)
					.build();
				JSONQL fmJQL = JSONQL.newInstance(fmSelect, fmProp.propModel().modelName(), fmWhere);
				String fmModelsJs = execut(fmProp.propModel(),fmJQL);
				JSONArray fmModels = new JSONArray(fmModelsJs);
				
				// 注入属性
				for(Object fmModel : fmModels) {
					JSONObject fmModelJson = (JSONObject)fmModel;
					
					Object modelObject = CollectionUtils.findOne(models, m -> {
						return ((JSONObject)m).get(fmProp.linkProp().propName()).toString()
							.equals(fmModelJson.get(fmProp.propModel().id().propName())
								.toString());
					});
					
					JSONObject modelJson = (JSONObject)modelObject;
					modelJson.set(fmProp.propName(), fmModelJson);
				}
			}
			
			// user userDetail[userId]
			// 这种情况可能会出现多个 注意判别
			else {
				// userIds
				List<Object> modelIds = new LinkedList<>();
				for (Map ctnRecord : modelCtnMap) {
					Object id = ctnRecord.get(
						fmProp.ofModel().id().ctnField().fieldName());
					modelIds.add(id);
				}
				
				// 构建搜索语句搜索属性模型
				Select fmSelect = ql.select().propSelect(fmProp.propName());
				fmSelect = Select.link(fmSelect, Select.builder()
					.props(fmProp.linkProp().propName()).build());
				Where fmWhere = Where.builder()
					.in(fmProp.linkProp().propName(), modelIds)
					.build();
				JSONQL fmJQL = JSONQL.newInstance(fmSelect, fmProp.propModel().modelName(), fmWhere);
				String fmModelsJs = execut(fmProp.propModel(),fmJQL);
				JSONArray fmModels = new JSONArray(fmModelsJs);
				
				// 注入属性
				if(fmProp.single()) {
					for(Object fmModel : fmModels) {
						JSONObject fmModelJson = (JSONObject)fmModel;
						
						Object modelObject = CollectionUtils.findOne(models, m -> {
							return ((JSONObject)m).get(model.id().propName()).toString()
								.equals(fmModelJson.get(fmProp.linkProp().propName()).toString());
						});
						
						JSONObject modelJson = (JSONObject)modelObject;
						modelJson.set(fmProp.propName(), fmModelJson);
					}
				}
				else {
					for(Object fmModel : fmModels) {
						JSONObject fmModelJson = (JSONObject)fmModel;
						
						Object modelObject = CollectionUtils.findOne(models, m -> {
							return ((JSONObject)m).get(model.id().propName()).toString()
								.equals(fmModelJson.get(fmProp.linkProp().propName()).toString());
						});
						
						JSONObject modelJson = (JSONObject)modelObject;
						if(!modelJson.containsKey(fmProp.propName())) {
							modelJson.set(fmProp.propName(), new JSONArray());
						}
						// 添加到集合中
						((JSONArray)modelJson.get(fmProp.propName())).add(fmModelJson);
					}
				}
			}
		}
		
		// 4 关系表链接的属性
		for (RltProp rltProp : model.listRltProps()) {
			// 如果搜索了外键链接模型 就补充外键链接模型的属性值
			if(!ql.select().containProp(rltProp.propName())) {
				continue;
			}
			
			// 获取模型的Id属性
			List<Object> modelIds = new LinkedList<>();
			for(Object mo:models) {
				Object id = ((JSONObject)mo).get(model.id().propName());
				modelIds.add(id);
			}
			
			// 构建搜索条件 搜索关系表
			// 从关联表中查出模型的id
			JSONQL rltInJql = JSONQL.builder()
				.select(select -> {
					select.props(rltProp.foreignField().fieldName(),
						rltProp.primaryField().fieldName());})
				.from(rltProp.rltCtn().ctnName())
				.where(where -> {
					where.in(rltProp.primaryField().fieldName(), modelIds);
				})
				.build();
			CtnQLExecutor rltExecutor = ctnExecutors.get(rltProp.rltCtn().ctnType());
			List<Map> idInMap =  rltExecutor.exe(rltInJql);
			List<Object> inIds = idInMap.stream().map(item -> {
				return item.get(rltProp.foreignField().fieldName());
			}).collect(Collectors.toList());
			
			// 根据Id搜索模型数据
			Select rltModelSelete = ql.select().propSelect(rltProp.propName());
			Where rltModelWhere = Where.builder()
				.in(rltProp.propModel().id().propName(), inIds)
				.build();
			JSONQL rltModelQL = JSONQL.newInstance(rltModelSelete,
				rltProp.propModel().modelName(), rltModelWhere);
			
			String rltModelsJs = execut(rltProp.propModel(),rltModelQL);
			JSONArray rltModels = new JSONArray(rltModelsJs);
			
			// 注入属性
			// 获取链接关系
			Map<String,List<String>> idRlts = new HashMap<>();
			for (Map idMap : idInMap) {
				Object primaryId = idMap.get(rltProp.primaryField());
				Object foreignId = idMap.get(rltProp.foreignField());
				
				if(!idRlts.containsKey(primaryId)) {
					idRlts.put(primaryId.toString(), new LinkedList<String>());
				}
				idRlts.get(primaryId.toString()).add(foreignId.toString());
			}
			for(Object m : models) {
				JSONObject modelJson = (JSONObject)m;
				List<String> rmIds = idRlts.get(modelJson.get(model.id().propName()));
				if(rltProp.single()) {
					if(rmIds.size() > 1) {
						// TODO 完善
						throw new ExecutException("标记为单个对象，但是存在多个");
					}
					
					Object rltModel = CollectionUtils.findOne(rltModels, rltObj -> {
						String rltPropId = ((JSONObject)rltObj)
							.get(rltProp.propModel().id().propName()).toString();
						return rmIds.contains(rltPropId);
					});
					
					// 添加到集合中
					((JSONArray)modelJson.get(rltProp.propName())).add(rltModel);
				}
				
				else {
					List<Object> rltModel = CollectionUtils.findAll(rltModels, rltObj -> {
						String rltPropId = ((JSONObject)rltObj)
							.get(rltProp.propModel().id().propName()).toString();
						return rmIds.contains(rltPropId);
					});
					
					// 添加到集合中
					((JSONArray)modelJson.get(rltProp.propName())).add(rltModel);
				}
			}
		}
		return models.toString();
	}
	
	private CtnProp getCtnPropByFieldName(Model model,String fieldName) {
		return CollectionUtils.findOne(model.listCtnProps(), ctnProp -> {
			return ctnProp.ctnField().fieldName().equals(fieldName);
		});
	}
}
